Skip to content

Conversation

@ruistola
Copy link
Contributor

@ruistola ruistola commented Mar 18, 2025

We're using go-selfupdate v.1.4.1 in our CLI project and the self-update fails on Windows with the following configuration:

Image

(not visible in the image, but there's 64GB of RAM on the system)

The error originates from selfupdate.ExecutablePath() and more specifically, from a call to filepath.EvalSymLinks()within. This function appears to be notoriously unreliable on Windows, see e.g. golang/go#42201

This PR introduces a new internal function internal.ResolvePath() as a replacement for direct filepath.EvalSymlinks() calls. The implementation uses x/sys/windows.GetFinalPathNameByHandle() on Windows, and filepath.EvalSymlinks() on unix(-like) platforms.

Summary by CodeRabbit

  • Chores

    • Refined dependency management by directly incorporating critical system dependencies.
  • New Features

    • Enhanced symbolic link resolution for executables and command paths.
    • Introduced platform-specific logic to ensure more reliable operation on both Unix and Windows environments.
    • Added a new method for resolving paths in both Unix and Windows environments.

@coderabbitai
Copy link

coderabbitai bot commented Mar 18, 2025

Walkthrough

This pull request converts an indirect dependency in the go.mod file to a direct requirement for golang.org/x/sys v0.29.0. It also replaces uses of filepath.EvalSymlinks with a new internal function ResolvePath in both the executable path retrieval and the update command. Two new files provide platform-specific implementations of ResolvePath for Unix and Windows systems.

Changes

Files Change Summary
go.mod Updated golang.org/x/sys v0.29.0: removed indirect entry and added as a direct dependency.
internal/path.go, update.go Replaced filepath.EvalSymlinks calls with internal.ResolvePath for resolving file paths.
internal/resolve_path_unix.go, internal/resolve_path_windows.go Added new ResolvePath(filename string) (string, error) implementations for Unix and Windows platforms.

Sequence Diagram(s)

sequenceDiagram
    participant Caller as Function Caller
    participant Resolver as internal.ResolvePath
    participant Platform as OS-Specific Resolver

    Caller->>Resolver: Invoke ResolvePath(filename)
    alt Unix
        Resolver->>Platform: Call filepath.EvalSymlinks(filename)
    else Windows
        Resolver->>Platform: Call windows.GetFinalPathNameByHandle(file handle)
    end
    Platform-->>Resolver: Return resolved path or error
    Resolver-->>Caller: Return result
Loading

Poem

I hopped through lines of code with glee,
New paths unveiled for all to see.
From indirect bounds to direct light,
My swift ResolvePath makes things right.
A bunny’s cheer—let’s keep hopping in flight!

Tip

⚡🧪 Multi-step agentic review comment chat (experimental)
  • We're introducing multi-step agentic chat in review comments. This experimental feature enhances review discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments.
    - To enable this feature, set early_access to true under in the settings.

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 567094a and 9b429f8.

📒 Files selected for processing (1)
  • internal/resolve_path_windows.go (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
internal/resolve_path_windows.go (1)
internal/resolve_path_unix.go (1) (1)
  • ResolvePath (10:17)
🔇 Additional comments (7)
internal/resolve_path_windows.go (7)

1-2: Windows build constraint is correctly set.
The //go:build windows directive ensures this file is only compiled on Windows.


5-10: Imports look appropriate.
These imports provide the necessary Windows APIs and standard libraries to manage file handles and path manipulation.


14-18: File opening may fail on directories.
If end-users pass a directory path, os.Open() might fail. Confirm whether directory path handling is in scope.


23-28: Initial probe for buffer size is a good approach.
Using GetFinalPathNameByHandle to get the needed buffer size before the actual call is a recommended pattern.


29-34: Properly allocating buffer and calling again.
This two-step buffer strategy ensures correct handling of Windows path lengths. Implementation looks solid.


38-40: Trimming '\?\' prefix is correct for user-friendly paths.
This ensures a consistent DOS path format is returned.


41-41: Returning the final resolved path.
The function meets its stated goal of returning a clean path.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
internal/resolve_path_windows.go (1)

1-33: Test edge cases with very long file paths.

This function relies on syscall.MAX_PATH, which may not handle extended Windows paths. Consider dynamic buffer sizing to accommodate paths beyond MAX_PATH.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e20b78b and 567094a.

📒 Files selected for processing (5)
  • go.mod (1 hunks)
  • internal/path.go (1 hunks)
  • internal/resolve_path_unix.go (1 hunks)
  • internal/resolve_path_windows.go (1 hunks)
  • update.go (1 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
internal/resolve_path_unix.go (1)
internal/resolve_path_windows.go (1) (1)
  • ResolvePath (13:32)
internal/resolve_path_windows.go (1)
internal/resolve_path_unix.go (1) (1)
  • ResolvePath (10:17)
🔇 Additional comments (4)
go.mod (1)

14-14: Confirm the chosen version.

Please verify whether golang.org/x/sys v0.29.0 is up-to-date and free of known issues.

internal/resolve_path_unix.go (1)

1-17: Straightforward Unix path resolution.

This is a clean wrapper around filepath.EvalSymlinks. Looks good.

internal/path.go (1)

14-14: Unified path resolution is a good approach.

Switching to ResolvePath ensures consistent cross-platform behavior. Looks good.

update.go (1)

57-62: Great replacement for Windows reliability
Switching from filepath.EvalSymlinks to internal.ResolvePath addresses known issues on Windows and retains behavior for Unix-like systems.

@codecov
Copy link

codecov bot commented Mar 18, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 77.07%. Comparing base (e20b78b) to head (9b429f8).
Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #46   +/-   ##
=======================================
  Coverage   77.07%   77.07%           
=======================================
  Files          28       28           
  Lines        1435     1435           
=======================================
  Hits         1106     1106           
  Misses        279      279           
  Partials       50       50           
Flag Coverage Δ
unittests 77.07% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@creativeprojects
Copy link
Owner

Thanks for this PR!

It looks good to me.

For my understanding, can you explain how to replicate this bug?
I usually like to be able to prove the fix inside a test if possible (for regression)

@ruistola
Copy link
Contributor Author

Thanks for this PR!

It looks good to me.

For my understanding, can you explain how to replicate this bug? I usually like to be able to prove the fix inside a test if possible (for regression)

Unfortunately I cannot repro this myself as I develop on macOS. This was a problem my colleague (the attached system info is from their laptop) was facing after having installed our CLI app (with Scoop) and trying to update it with the self-update feature.

We quickly narrowed the issue down to filepath.EvalSymlinks() returning an error. Tried both Powershell and Cmd, running as administrator and without, same result, fail every time. Only after replacing filepath.EvalSymlinks() with a completely different implementation did the self-update start to work on that particular system.

I wish I could be of more help but we really never nailed down which particular detail in the system made it fail. Searching for relevant issues in the golang repo itself didn't help much, either. The main takeaway I learned from there was just to avoid using filepath.EvalSymlinks() altogether on Windows. 😅

@creativeprojects
Copy link
Owner

Thanks for the information!
I'll try scoop and see how it installs binaries, it might be doing a link to its own installation directory or something.

As a side note, I usually disable self-updating when installing from package managers. They can get annoyed when they find out the binary has changed 😆

@creativeprojects
Copy link
Owner

creativeprojects commented Mar 18, 2025

So, after some investigation, I can see scoop is using a junction to link the current directory to the latest version.

So far so good:

$ dir                                                                                     
 Volume in drive C is OS                                                                  
 Volume Serial Number is 7362-F213                                                        
                                                                                          
 Directory of c:\Users\cp\scoop\apps\restic                                            
                                                                                          
18/03/2025  21:02    <DIR>          .                                                     
18/03/2025  21:03    <DIR>          ..                                                    
18/03/2025  21:02    <DIR>          0.17.3                                                
18/03/2025  21:02    <JUNCTION>     current [\??\C:\Users\cp\scoop\apps\restic\0.17.3] 
               0 File(s)              0 bytes                                             
               4 Dir(s)  175,534,452,736 bytes free                                       
                                                                                          

I thought: oh well that's going to be easy to replicate in a test.
But that's the thing, the filepath.EvalSymlink actually works fine here 🤔

As a matter of fact, yours also works fine so I'll accept the PR 👍🏻

Thank you 😉

@creativeprojects creativeprojects merged commit 510953b into creativeprojects:main Mar 19, 2025
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants